﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using msilgc.cbuilder.CSyntaxTree;
using msilgc.cbuilder.MSILSyntaxTree;
using msilgc.CSyntaxTree;

namespace msilgc.cbuilder.CBuilder
{
    internal class ConditionalBranchTranslator : ITranslator
    {
        private readonly IDictionary<OpCode, Operator> _operators =
            new Dictionary<OpCode, Operator>
                {
                    {OpCodes.Beq, Operator.Eq},
                    {OpCodes.Beq_S, Operator.Eq},
                    {OpCodes.Bge, Operator.Ge},
                    {OpCodes.Bge_S, Operator.Ge},
                    {OpCodes.Bge_Un, Operator.Ge},
                    {OpCodes.Bge_Un_S, Operator.Ge},
                    {OpCodes.Bgt, Operator.Gt},
                    {OpCodes.Bgt_S, Operator.Gt},
                    {OpCodes.Bgt_Un, Operator.Gt},
                    {OpCodes.Bgt_Un_S, Operator.Gt},
                    {OpCodes.Ble, Operator.Le},
                    {OpCodes.Ble_S, Operator.Le},
                    {OpCodes.Ble_Un, Operator.Le},
                    {OpCodes.Ble_Un_S, Operator.Le},
                    {OpCodes.Blt, Operator.Lt},
                    {OpCodes.Blt_S, Operator.Lt},
                    {OpCodes.Blt_Un, Operator.Lt},
                    {OpCodes.Blt_Un_S, Operator.Lt},
                    {OpCodes.Bne_Un, Operator.Ne},
                    {OpCodes.Bne_Un_S, Operator.Ne},
                };

        private void CheckInstruction(Instruction inst) {
            if(!_operators.ContainsKey(inst.Inst)) {
                throw new Exception("unexpected instruction: " + inst);
            }
        }

        public ICStatement TranslateAndAdd(
            CMethodBody methodBody, CBasicBlock currentBlock, BasicBlock block, Instruction instruction)
        {
            CheckInstruction(instruction);
            var targetBlock = methodBody.BasicBlocks.First(cBlock => block.TargetsThatAreNotTheNextBlock.Contains(cBlock.MsilBlock));
            var v1 = EvaluationStackHelper.PopVariableOffEvaluationStack(currentBlock);
            var v2 = EvaluationStackHelper.PopVariableOffEvaluationStack(currentBlock);
            var stm = new ConditionalBranchStatement(_operators[instruction.Inst], targetBlock.Label, v1, v2);
            currentBlock.Statements.Add(stm);
            return stm;
        }
    }
}
